namespace MSU_WebServices
{
    using System.Web.Services;
    using MSU_Broker;
    using System;
    using System.DirectoryServices;
    /// <summary>
    /// Summary description for ChangeData
    /// </summary>
    public partial class WSMSU : System.Web.Services.WebService
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="objAdm"></param>
        /// <param name="objUser"></param>
        /// <param name="newPassword"></param>
        [WebMethod]
        public void ChangeUserPassword(MSU_Adm objAdm, MSU_User objUser, string Password, string newPassword)
        {
            try
            {
                try
                {
                    ChangeUserPasswordAD(objAdm, objUser, newPassword);
                }
                catch (Exception e)
                {
                    throw e;
                }
                try
                {
                    ChangeUserPasswordMoodle(objAdm, objUser, newPassword);
                }
                catch (Exception e)
                {
                    try
                    {
                        ChangeUserPasswordAD(objAdm, objUser, Password);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("Error on rollback LDAP user password.", ex);
                    }
                    throw e;
                }
            }
            catch (Exception ex)
            {
                throw MSU_ExceptionPack.PackSOAP("ChangePassword", "http://tempuri.org/MSUWS", MSU_ExceptionPack.FaultCode.Server, ex, ErrorsToScreen);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="administrator"></param>
        /// <param name="objUser"></param>
        /// <param name="userPassword"></param>
        /// <param name="userNewPassword"></param>
        private void ChangeUserPasswordAD(MSU_Adm administrator, MSU_User objUser, string Password)
        {
            try
            {
                string connectionPrefix = "LDAP://" + administrator.LdapPath1;
                string AdministratorUsername = administrator.AdUser;
                string AdministratorPassword = administrator.AdPass;
                SearchResult EntrySearchResult;
                DirectorySearcher searcher;
                using (DirectoryEntry entry = new DirectoryEntry(connectionPrefix, AdministratorUsername, AdministratorPassword, AuthenticationTypes.Secure))
                {
                    try
                    {
                        object nativeObject = entry.NativeObject;
                    }
                    catch (DirectoryServicesCOMException e)
                    {
                        if (e.ErrorCode.Equals(-2146893044))
                            throw new MSU_Exception("Invalid LDAP administrator username/password.", MSU_Error.INVALID_LDAP_ADMINISTRATOR_USERNAME_PASSWORD);
                    }
                    catch (System.Runtime.InteropServices.ExternalException e)
                    {
                        if (e.ErrorCode.Equals(-2147016672) || e.ErrorCode.Equals(-2147463168))
                            throw new MSU_Exception("An invalid LDAP directory pathname was passed.", MSU_Error.INVALID_LDAP_DIRECTORY_PATHNAME);
                        else if (e.ErrorCode.Equals(-2147016646))
                            throw new MSU_Exception("Invalid or offline LDAP server.", MSU_Error.LDAP_SERVER_NOT_FOUND);
                        else
                            throw e;
                    }
                    try
                    {
                        using (DirectoryEntry updateEntry = entry.Children.Find("CN=" + objUser.UserName + ",CN=Users"))
                        {
                            searcher = new DirectorySearcher(updateEntry);
                            searcher.Filter = "(objectclass=user)";
                            searcher.PropertiesToLoad.Add("nTSecurityDescriptor");
                            searcher.PropertiesToLoad.Add("userAccountControl");
                            searcher.CacheResults = false;
                            EntrySearchResult = searcher.FindOne();

                            if (EntrySearchResult == null)
                                throw new MSU_Exception("LDAP user account does not exists.", MSU_Error.LDAP_USER_ACCOUNT_DOES_NOT_EXISTS);

                            using (DirectoryEntry userEntry = EntrySearchResult.GetDirectoryEntry())
                            {
                                if (userEntry.Properties["ntSecurityDescriptor"].Count > 0)
                                {
                                    ActiveDs.IADsSecurityDescriptor ntSecurityDescriptor = (ActiveDs.IADsSecurityDescriptor)userEntry.Properties["ntSecurityDescriptor"][0];
                                    ActiveDs.IADsAccessControlList controlList = (ActiveDs.IADsAccessControlList)ntSecurityDescriptor.DiscretionaryAcl;

                                    foreach (ActiveDs.IADsAccessControlEntry aclEntry in controlList)
                                    {
                                        if (aclEntry.ObjectType != null)
                                        {
                                            //PASSWD_CANT_CHANGE attribute Guid = {AB721A53-1E2F-11D0-9819-00AA0040529B}
                                            if (aclEntry.ObjectType.ToUpper() == "{AB721A53-1E2F-11D0-9819-00AA0040529B}" && aclEntry.Trustee.Contains("NT") && aclEntry.Trustee.Contains("SELF"))
                                            {
                                                if (aclEntry.AceType == (int)ActiveDs.ADS_ACETYPE_ENUM.ADS_ACETYPE_ACCESS_DENIED_OBJECT)
                                                {
                                                    throw new MSU_Exception("LDAP user cannot change password.", MSU_Error.LDAP_USER_CANNOT_CHANGE_PASSWORD);
                                                }
                                            }
                                        }
                                    }
                                }

                                try
                                {
                                    updateEntry.Invoke("SetPassword", new object[] { Password });
                                    updateEntry.CommitChanges();
                                }
                                catch (System.Reflection.TargetInvocationException e)
                                {
                                    if (e.InnerException == null)
                                        throw e;
                                    System.Runtime.InteropServices.COMException ce;
                                    if (e.InnerException is System.Runtime.InteropServices.COMException)
                                    {
                                        ce = (System.Runtime.InteropServices.COMException)e.InnerException;
                                        if (ce.ErrorCode.Equals(-2147022651))
                                        {
                                            throw new MSU_Exception("LDAP user account Password does not match complexity requirements.", MSU_Error.LDAP_USER_ACCOUNT_PASSWORD_COMPLEXITY);
                                        }
                                        else
                                            throw ce;
                                    }
                                    else
                                        throw e.InnerException;
                                }
                            }
                        }
                    }
                    catch (System.Runtime.InteropServices.ExternalException e)
                    {
                        if (e.ErrorCode.Equals(-2147016656))
                            throw new MSU_Exception("LDAP user account does not exists.", MSU_Error.LDAP_USER_ACCOUNT_DOES_NOT_EXISTS);
                        else
                            throw e;
                    }
                }
            }
            catch (Exception e)
            {
                if (e is System.UnauthorizedAccessException)
                {
                    throw new MSU_Exception("Given LDAP administrator user account does not have sufficient privileges.", MSU_Error.LDAP_ADMINISTRATOR_USER_ACCOUNT_ACCESS_PERMISSIONS);
                }
                else
                    throw e;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="objUser"></param>
        /// <param name="Username"></param>
        /// <param name="Password"></param>
        /// <param name="adm"></param>
        public void ChangeUserPasswordMoodle(MSU_Adm adm, MSU_User objUser, string Password)
        {
            string Username = objUser.UserName;
            string UsernameADM = adm.MoodleUser;
            string PasswordADM = adm.MoodlePass;
            object loginReturn = null;
            try
            {
                loginReturn = callMethod("login", UsernameADM, PasswordADM);
            }
            catch (MSU_Exception e)
            {
                throw e;
            }
            catch (Exception e)
            {
                throw e;
            }

            string client = "";
            string sessionkey = "";
            try
            {
                client = getProperty(loginReturn, "client").ToString();
                sessionkey = getProperty(loginReturn, "sessionkey").ToString();
            }
            catch (Exception e)
            {
                throw new MSU_Exception("Error on retrieve client/sessionkey from Moodle login data.", MSU_Error.RETRIEVE_MOODLE_LOGIN_DATA, e);
            }
            if (client == "")
                throw new MSU_Exception("Invalid Moodle Administrator username/password.", MSU_Error.INVALID_MOODLE_ADMINISTRATOR_USERNAME_PASSWORD);

            Object editUsersOutput = null;
            try
            {
                Object getUsersReturn = callMethod("get_user", client, sessionkey, Username, "username");

                Object currentUserRecord = ((Array)getProperty(getUsersReturn, "users")).GetValue(0);

                Object userDatum = Activator.CreateInstance(serviceTypes["userDatum"]);

                setProperty(userDatum, "action", "Update");
                setProperty(userDatum, "username", Username);
                setProperty(userDatum, "idnumber", getProperty(currentUserRecord, "id"));
                setProperty(userDatum, "password", CalculateMD5Hash(Password));

                Array editUsersInput_users = Array.CreateInstance(serviceTypes["userDatum"], 1);
                editUsersInput_users.SetValue(userDatum, 0);

                Object editUsersInput = Activator.CreateInstance(serviceTypes["editUsersInput"]);
                setProperty(editUsersInput, "users", editUsersInput_users);

                editUsersOutput = callMethod("edit_users", client, sessionkey, editUsersInput);

            }
            catch (Exception e)
            {
                throw new Exception("Error on Moodle account password change.", e);
            }
            String users_output_error = "";
            try
            {
                Array users_output = (Array)getProperty(editUsersOutput, "users");
                users_output_error = (String)getProperty(users_output.GetValue(0), "error");
            }
            catch (Exception e)
            {
                throw new Exception("Error on check Moodle account password change errors.", e);
            }
            if (users_output_error != "")
                throw new Exception("WebService error on change Moodle account password: " + users_output_error);
            try
            {
                Object logoutReturn = callMethod("logout", client, sessionkey);
            }
            catch (Exception e)
            {
                throw new MSU_Exception("Error on Moodle administrator logout.", MSU_Error.MOODLE_ADMINISTRATOR_LOGOUT, e);
            }
        }
    }
}
